//
//  Promise.swift
//  XHPromise
//
//  Created by xinph on 2020/4/13.
//  Copyright © 2020 xinph. All rights reserved.
//  用法同js-Promise
//

import Foundation

enum State<T, E> {
    case pending
    case resolve(_ value: T)
    case reject(_ error: E)
}

struct Closure<T, E> {
    var resolve: ((T) -> Void)?
    var reject:((E) -> Void)?

    /// 闭包默认在主队列执行
    var onQueue = DispatchQueue.main
}

extension Closure {
    func callResolve(_ value: T) {
        onQueue.async {
            self.resolve?(value)
        }
    }
    
    func callReject(_ error: E) {
        onQueue.async {
            self.reject?(error)
        }
    }
}

protocol PromiseProtocol {
    associatedtype Element
    associatedtype U
    
    typealias ResolveType = (Element) -> Void
    typealias RejectType = (U) -> Void

    /// 该方法用来执行异步任务
    /// - Parameter excutor: 回执
    init(excutor: (_ resolve: @escaping ResolveType, _ reject: @escaping RejectType) -> Void)
    
    /// 该方法用来处理同步任务
    /// - Parameter resolve: 业务值
    init(value: Element)
    
    /// 该方法用来处理同步被拒任务
    /// - Parameter reject: 失败对象
    init(error: U)
}

class Promise<Element, U>: PromiseProtocol {
    
    /// 默认pending状态
    var state = State<Element, U>.pending
    /// closure集合
    var closures = [Closure<Element, U>]()
    /// 该队列所在线程只处理一些简单的赋值，数组添加
    private var serialQueue: DispatchQueue
    init() {
        serialQueue = DispatchQueue(label: "PromiseQueue")
    }
    
    required convenience init(excutor: (_ resolve: @escaping ResolveType, _ reject: @escaping RejectType) -> Void) {
        self.init()
        //立即执行
        excutor(resolve, reject)
    }
    
    required convenience init(value: Element) {
        self.init()
        state = .resolve(value)
    }
    
    required convenience init(error: U) {
        self.init()
        state = .reject(error)
    }
}

extension Promise {
    
    /// 标记状态为resolve，然后执行resolve回执
    /// - Parameter value: 外面传递进来的需要处理的业务数据
    private func resolve(_ value: Element) {
        self.state = .resolve(value)
        fireClosure()
    }
    
    private func reject(_ error: U) {
        self.state = .reject(error)
        fireClosure()
    }
    
    /// 执行所有添加进来的回执
    private func fireClosure() {
        switch state {
        case .pending:
            return
        case .resolve(let value):
            serialQueue.async {
                self.closures.forEach {$0.callResolve(value)}
            }
        case .reject(let error):
            serialQueue.async {
                self.closures.forEach {$0.callReject(error)}
            }
        }
    }
    
    /// 将闭包添加进入组
    /// - Parameter closure: `then` `catch`添加进来的闭包
    private func addClosure(_ closure: Closure<Element, U>) {
        serialQueue.async {
            self.closures.append(closure)
        }
        fireClosure()
    }
}

extension Promise {
    @discardableResult
    func then(on queue: DispatchQueue = DispatchQueue.main, resolve: @escaping (Element) -> Void) -> Promise<Element, U> {
        serialQueue.async {
            self.addClosure(Closure(resolve: resolve, onQueue: queue))
        }
        return self
    }
    
    @discardableResult
    func `catch`(on queue: DispatchQueue = DispatchQueue.main, reject: @escaping (U)->Void) -> Promise<Element, U> {
        serialQueue.async {
            self.addClosure(Closure(reject: reject, onQueue: queue))
        }
        return self
    }
}
